
;数据段，用来存储数据
DATA SEGMENT
    ;欢迎界面
    HELLOPIC    DB '           ',0DH,0AH
                DB '           ',0DH,0AH
                DB '           ',0DH,0AH
                DB '            /##      /##       /######       /##   /##       /########',0DH,0AH
                DB '           | ###    /###      |_  ##_/      | ### | ##      | ##_____/',0DH,0AH
                DB '           | ####  /####        | ##        | ####| ##      | ##      ',0DH,0AH
                DB '           | ## ##/## ##        | ##        | ## ## ##      | #####   ',0DH,0AH
                DB '           | ##  ###| ##        | ##        | ##  ####      | ##__/   ',0DH,0AH
                DB '           | ##\  # | ##        | ##        | ##\  ###      | ##      ',0DH,0AH
                DB '           | ## \/  | ##       /######      | ## \  ##      | ########',0DH,0AH
                DB '           |__/     |__/      |______/      |__/  \__/      |________/',0DH,0AH
                DB '                  /#######        /#######         /######            ',0DH,0AH
                DB '                 | ##__  ##      | ##__  ##       /##__  ##           ',0DH,0AH
                DB '                 | ##  \ ##      | ##  \ ##      | ##  \ ##           ',0DH,0AH
                DB '                 | #######/      | #######/      | ##  | ##           ',0DH,0AH
                DB '                 | ##____/       | ##__  ##      | ##  | ##           ',0DH,0AH
                DB '                 | ##            | ##  \ ##      | ##  | ##           ',0DH,0AH
                DB '                 | ##            | ##  | ##      |  ######/           ',0DH,0AH
                DB '                 |__/            |__/  |__/       \______/            ',0DH,0AH
                DB '         ',0DH,0AH
                DB '         ',0DH,0AH
                DB '                            press any key to start                    ',0DH,0AH
                DB '$'
    ;难度选择界面
    HARD_CHOICEPIC  DB '                           __  __  ______  _   _  _    _                ',0DH,0AH
                    DB '                          |  \/  ||  ____|| \ | || |  | |               ',0DH,0AH
                    DB '                          | \  / || |__   |  \| || |  | |               ',0DH,0AH
                    DB '                          | |\/| ||  __|  | . ` || |  | |               ',0DH,0AH
                    DB '                          | |  | || |____ | |\  || |__| |               ',0DH,0AH
                    DB '                          |_|  |_||______||_| \_| \____/                ',0DH,0AH
                    DB '                           _____  _____  __  __  _____   _       ______ ',0DH,0AH
                    DB '                /\        / ____||_   _||  \/  ||  __ \ | |     |  ____|',0DH,0AH
                    DB '               /  \      | (___    | |  | \  / || |__) || |     | |__   ',0DH,0AH
                    DB '              / /\ \      \___ \   | |  | |\/| ||  ___/ | |     |  __|  ',0DH,0AH
                    DB '             / ____ \  _  ____) | _| |_ | |  | || |     | |____ | |____ ',0DH,0AH
                    DB '            /_/    \_\(_)|_____/ |_____||_|  |_||_|     |______||______|',0DH,0AH
                    DB '             ____     _   _   ____   _____   __  __            _        ',0DH,0AH
                    DB '            |  _ \   | \ | | / __ \ |  __ \ |  \/  |    /\    | |       ',0DH,0AH
                    DB '            | |_) |  |  \| || |  | || |__) || \  / |   /  \   | |       ',0DH,0AH
                    DB '            |  _ <   | . ` || |  | ||  _  / | |\/| |  / /\ \  | |       ',0DH,0AH
                    DB '            | |_) |_ | |\  || |__| || | \ \ | |  | | / ____ \ | |____   ',0DH,0AH
                    DB '            |____/(_)|_| \_| \____/ |_|  \_\|_|  |_|/_/    \_\|______|  ',0DH,0AH
                    DB '              _____     _    _            _____   _____                 ',0DH,0AH
                    DB '             / ____|   | |  | |    /\    |  __ \ |  __ \                ',0DH,0AH
                    DB '            | |        | |__| |   /  \   | |__) || |  | |               ',0DH,0AH
                    DB '            | |        |  __  |  / /\ \  |  _  / | |  | |               ',0DH,0AH
                    DB '            | |____  _ | |  | | / ____ \ | | \ \ | |__| |               ',0DH,0AH
                    DB '             \_____|(_)|_|  |_|/_/    \_\|_|  \_\|_____/                ',0DH,0AH
                    DB '$'
    ;胜利界面
    WIN_PIC         DB '            /##     /##/######  /##   /## ',0DH,0AH
                    DB '           |  ##   /##/##__  ##| ##  | ## ',0DH,0AH
                    DB '            \  ## /##/ ##  \ ##| ##  | ## ',0DH,0AH
                    DB '             \  ####/| ##  | ##| ##  | ## ',0DH,0AH
                    DB '              \  ##/ | ##  | ##| ##  | ## ',0DH,0AH
                    DB '               | ##  | ##  | ##| ##  | ## ',0DH,0AH
                    DB '               | ##  |  ######/|  ######/ ',0DH,0AH
                    DB '               |__/   \______/  \______/  ',0DH,0AH
                    DB '            /##      /## /###### /##   /##',0DH,0AH
                    DB '           | ##  /# | ##|_  ##_/| ### | ##',0DH,0AH
                    DB '           | ## /###| ##  | ##  | ####| ##',0DH,0AH
                    DB '           | ##/## ## ##  | ##  | ## ## ##',0DH,0AH
                    DB '           | ####_  ####  | ##  | ##  ####',0DH,0AH
                    DB '           | ###/ \  ###  | ##  | ##\  ###',0DH,0AH
                    DB '           | ##/   \  ## /######| ## \  ##',0DH,0AH
                    DB '           |__/     \__/|______/|__/  \__/',0DH,0AH
                    DB '                    PRESS J TO RESTART',0DH,0AH
                    DB '                    PRESS K TO QUIT',0DH,0AH
                    DB '$'
    ;失败界面
    LOOSE_PIC       DB '            /##     /##/######  /##   /##',0DH,0AH
                    DB '           |  ##   /##/##__  ##| ##  | ##',0DH,0AH
                    DB '            \  ## /##/ ##  \ ##| ##  | ##',0DH,0AH
                    DB '             \  ####/| ##  | ##| ##  | ##',0DH,0AH
                    DB '              \  ##/ | ##  | ##| ##  | ##',0DH,0AH
                    DB '               | ##  | ##  | ##| ##  | ##',0DH,0AH
                    DB '               | ##  |  ######/|  ######/',0DH,0AH
                    DB '               |__/   \______/  \______/ ',0DH,0AH
                    DB '            /#######  /###### /########  ',0DH,0AH
                    DB '           | ##__  ##|_  ##_/| ##_____/  ',0DH,0AH
                    DB '           | ##  \ ##  | ##  | ##        ',0DH,0AH
                    DB '           | ##  | ##  | ##  | #####     ',0DH,0AH
                    DB '           | ##  | ##  | ##  | ##__/     ',0DH,0AH
                    DB '           | ##  | ##  | ##  | ##        ',0DH,0AH
                    DB '           | #######/ /######| ########  ',0DH,0AH
                    DB '           |_______/ |______/|________/  ',0DH,0AH
                    DB '                  PRESS J TO RESTART',0DH,0AH
                    DB '                  PRESS K TO QUIT',0DH,0AH
                    DB '$'
    ;时间耗尽界面
    TIME_OUT_PIC    DB '            /########       /######       /##      /##       /########',0DH,0AH
                    DB '           |__  ##__/      |_  ##_/      | ###    /###      | ##_____/',0DH,0AH
                    DB '              | ##           | ##        | ####  /####      | ##      ',0DH,0AH
                    DB '              | ##           | ##        | ## ##/## ##      | #####   ',0DH,0AH
                    DB '              | ##           | ##        | ##  ###| ##      | ##__/   ',0DH,0AH
                    DB '              | ##           | ##        | ##\  # | ##      | ##      ',0DH,0AH
                    DB '              | ##          /######      | ## \/  | ##      | ########',0DH,0AH
                    DB '              |__/         |______/      |__/     |__/      |________/',0DH,0AH
                    DB '                   /######        /##   /##       /########           ',0DH,0AH
                    DB '                  /##__  ##      | ##  | ##      |__  ##__/           ',0DH,0AH
                    DB '                 | ##  \ ##      | ##  | ##         | ##              ',0DH,0AH
                    DB '                 | ##  | ##      | ##  | ##         | ##              ',0DH,0AH
                    DB '                 | ##  | ##      | ##  | ##         | ##              ',0DH,0AH
                    DB '                 | ##  | ##      | ##  | ##         | ##              ',0DH,0AH
                    DB '                 |  ######/      |  ######/         | ##              ',0DH,0AH
                    DB '                  \______/        \______/          |__/               ',0DH,0AH
                    DB '           PRESS J TO RESTART',0DH,0AH
                    DB '           PRESS K TO QUIT',0DH,0AH
                    DB '$'
    ;打印剩余雷数
    MINE_LEFT_PIC   DB 'MINE_LEFT:','$'
    ;打印用时
    SECOND_USED_PIC   DB 'SECOND_LEFT:','$'
    ;主数据，存储地雷的位置，有雷的地方0FFH，没有雷0H，图大小为10*20的二维数组
    MINE_TABLE      DB  10*20  DUP(0H)
    ;判断表，记录第i行第j列的元素四周雷的个数,取值0~8
    JUDGE_TABLE     DB  10*20  DUP(0H)
    ;可见表，记录已经被扫开的区域为FFH，特别的用0FH记录标记为雷的区域，不可见区域标记为0H
    VISIBLE_TABLE   DB  10*20  DUP(0H)
    ;未排地雷个数
    MINE_LEFT       DB  0H
    ;总雷数
    MINE_NUM        DB  0H
    ;难度，分为三个等级:分别为0,1,2
    ;A.10*10,10个雷,80秒
    ;B.10*20,40个雷,80秒
    ;C.10*20,80个雷,40秒
    HARD_LEVEL      DB  0H
    ;真正的宽高
    REAL_HEIGHT     DW  0H
    REAL_WIDTH      DW  0H
    REAL_SIZE       DW  0H
    ;开始的分钟数，秒数
    START_MIN       DB  0H
    START_SECOND    DB  0H
    ;当前分钟数，秒数
    NOW_MIN         DB  0H
    NOW_SECOND      DB  0H
    ;已用秒数
    SECOND_USED     DW 0H
    ;最大秒数
    MAX_SECOND      DW  0H
    ;###########属性数据###########
    ;当前处于表中位置
    TABLE_X         DW  0H
    TABLE_Y         DW  0H
    ;临时变量
    TEMP_X          DW  0H
    TEMP_Y          DW  0H
    ;标志变量
    FLAG            DW  0H
    ;一些颜色常量
    BLUE            EQU 00011111B
    WHITE           EQU 01111001B
    GRAY            EQU 00111111B
    GREEN           EQU 00101100B
    RED             EQU 11001111B
    ;声音代码
    SOUNDCODE       DW 0
    ;音效：频率和节拍
    MUSIC_1_FREG DW 2 DUP (578),-1;音乐频率表
    MUSIC_1_TIME DW 3 DUP (35);音乐节拍表
    MUSIC_2_FREG DW 2 DUP (440),-1
    MUSIC_2_TIME DW 3 DUP (35)
    MUSIC_3_FREG DW 2 DUP (392),-1
    MUSIC_3_TIME DW 3 DUP (35)
    MUSIC_4_FREG DW 2 DUP (523),-1
    MUSIC_4_TIME DW 3 DUP (35)
DATA ENDS

;附加段，用来保存宏
EXTRA SEGMENT

    ;获取当前时间
    GET_TIME MACRO MIN,SECOND
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        MOV AH,2CH ;2ch号功能调用，取系统时间：ch,cl,dH中分别存放时分秒
		INT 21H
        MOV SECOND,DH
        MOV MIN,CL
        POP DX
        POP CX
        POP BX
        POP AX
    ENDM

    ;计算已用秒数
    ;逻辑非常简单，就是分钟差*60+秒数差
    GET_SECOND_USED MACRO
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        MOV AL,NOW_MIN
        SUB AL,START_MIN
        MOV BL,60
        MUL BL
        MOV BL,NOW_SECOND
        XOR BH,BH
        ADD AX,BX
        MOV BL,START_SECOND
        XOR BH,BH
        SUB AX,BX
        MOV SECOND_USED,AX
        POP DX
        POP CX
        POP BX
        POP AX
    ENDM

    ;生成1-799的随机数，存储在BX中
    ;DW RESULT
    GET_RAND MACRO
        PUSH CX
        PUSH DX
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN AL, DX   ;产生的随机数AL
        MOV BL,AL
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN AL, DX   ;产生的随机数AL
        ADD BL,AL
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN AL, DX   ;产生的随机数AL
        ADD BL,AL
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN AL, DX   ;产生的随机数AL
        MOV BH,AL
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN  AL, DX   ;产生的随机数AL
        ADD BH,AL
        MOV AX, 50   ;产生从1到AX之间的随机数
        MOV DX, 41H ;用端口41H，或者上面说的其他端口也行
        OUT DX, AX  ;有这句话后，我发现就可以产生从1到AX之间的随机数了
        IN  AL, DX   ;产生的随机数AL
        ADD BH,AL
        MOV AX,BX
        MOV CX,REAL_SIZE
        MOV DX,0
        DIV CX
        MOV BX,DX
        POP DX
        POP CX
    ENDM

    SET_MINE_NUM MACRO HARD_LEVEL,MINE_NUM     ;对MINE_NUM初始化
        CMP HARD_LEVEL,0
        JE SET_EX_MINE_NUM1
        CMP HARD_LEVEL,1
        JE SET_EX_MINE_NUM2
        CMP HARD_LEVEL,2
        JE SET_EX_MINE_NUM3

        SET_EX_MINE_NUM1:
            MOV MINE_NUM,10
            MOV REAL_HEIGHT,10
            MOV REAL_WIDTH,10
            MOV REAL_SIZE,100
            MOV MAX_SECOND,80
            JMP END_SET_MINE_NUM

        SET_EX_MINE_NUM2:
            MOV MINE_NUM,40
            MOV REAL_HEIGHT,10
            MOV REAL_WIDTH,20
            MOV REAL_SIZE,200
            MOV MAX_SECOND,80
            JMP END_SET_MINE_NUM
        
        SET_EX_MINE_NUM3:
            MOV MINE_NUM,80
            MOV REAL_HEIGHT,10
            MOV REAL_WIDTH,20
            MOV REAL_SIZE,200
            MOV MAX_SECOND,40
            JMP END_SET_MINE_NUM
        
        END_SET_MINE_NUM:
        ENDM


    ;设置屏幕分辨率80*25 16色文本方式,隐藏光标
    SETSCREEN MACRO
        MOV AH,0    
        MOV AL,03H  ;80*25 16色文本
        INT 10H
        ENDM
    
    ;根据键入获取难度等级，存储在HARD_LEVEL中
    GET_HARD_LEVEL MACRO HARD_LEVEL
        ;键入
        BEGIN:
        MOV AH,0
        INT 16H
        ;判断键入字符是否在a-c之间，若不在循环键入
        CMP AL,'a'
        JL  BEGIN
        CMP AL,'c'
        JG  BEGIN
        ;把AL-'a'的值存在HARD_LEVEL中
        SUB AL,'a'
        MOV HARD_LEVEL,AL
        ENDM

    ;########画方形宏########
    ;参数：DW ROW,DW COL,DB CHAR,DB BG_COLOR
    ;功能在(COL,ROW)位置画一个正方形，左边显示相应字符
    DRAW_SQUARE MACRO ROW,COL,CHAR,BG_COLOR
        ;保存信息
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        ;移动光标
        MOV AH,2
        MOV BH,0
        MOV DH,BYTE PTR ROW
        MOV DL,BYTE PTR COL
        INT 10H
        ;连续打印两个字符，第一个为CHAR，第二个为' '
        ;打印CHAR
        mov ah,09h
        mov al,CHAR   ;显示的字符
        mov cx,1    ;字符显示数量
        mov bl,BG_COLOR ;字符颜色信息
        mov bh,00  ;页码设置
        int 10h    ;打印字符
         ;移动光标
        MOV AH,2
        MOV BH,0
        MOV DH,BYTE PTR ROW
        MOV DL,BYTE PTR COL
        INC DL
        INT 10H
        ;打印' '
        mov ah,09h
        mov al,' '   ;显示的字符
        mov cx,1    ;字符显示数量
        mov bl,BG_COLOR ;字符颜色信息
        mov bh,00  ;页码设置
        int 10h    ;打印字符
        ;恢复信息
        POP DX
        POP CX
        POP BX
        POP AX
    ENDM

    ;########根据[ROW][COL]获得地址，存放在SI中，DI为0表示越界########
    ;参数：DW ROW,DW COL
    POSITION_REC MACRO ROW,COL
        PUSH DX
        PUSH CX
        PUSH AX
        ;SI=ROW*40+COL
        MOV AX,REAL_WIDTH
        MUL ROW
        MOV SI,AX
        ADD SI,COL
        CALL CHECK_OUT_BOUND
        POP AX
        POP CX
        POP DX
    ENDM

    ;#######键入鼠标的宏########
    ;主要功能是键入鼠标，物理坐标保存到SCREEN_X,SCREEN_Y,把左右键保存到SI里
    ;SI,0是左键,FFFFH是右键
    ;参数:DW SCREEN_X,DW SCREEN_Y
    GET_MOUSE MACRO SCREEN_X,SCREEN_Y
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        ;调用33H中断
        ;入口参数：AX＝03H
        ;出口参数：BX＝按键状态：位0=1——按下左键位,1=1——按下右键位,2=1——按下中键
        MOV AX,03H
        INT 33H
        ; JMP END_ALL
        AND BX,00000011B
        ;保存横纵坐标
        ; MOV AX,CX
        ; DIV 16
        MOV SCREEN_X,CX
        MOV SCREEN_Y,DX
        ;检查是否为左键
        CMP BX,00000001B
        JZ  LEFT_MOUSE1
        CMP BX,00000010B
        JZ  RIGHT_MOUSE1
        MOV SI,2
        JMP END_GET_MOUSE
        RIGHT_MOUSE1:
            ;右键置SI为FFFFH
            MOV SI,0FFFFH
            JMP END_GET_MOUSE
        LEFT_MOUSE1:
            ;左键置SI为0
            MOV SI,0
            JMP END_GET_MOUSE
        END_GET_MOUSE:
        POP DX
        POP CX
        POP BX
        POP AX
    ENDM

    ;#######物理坐标逻辑坐标转换宏########
    POS_TURN MACRO  TABLE_X,TABLE_Y
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        ;将TABLE_X,TABLE_Y模16
        MOV AX,TABLE_X
        MOV BL,8
        DIV BL
        MOV BL,AL
        XOR BH,BH
        MOV TABLE_X,BX
        MOV AX,TABLE_Y
        MOV BL,16
        DIV BL
        MOV BL,AL
        XOR BH,BH
        MOV TABLE_Y,BX
        POP DX
        POP CX
        POP BX
        POP AX
    ENDM
     
EXTRA ENDS

STACK_SEG SEGMENT STACK
    MINE_STACK      DB  2000  DUP(0H)
STACK_SEG ENDS

;代码段，程序主指令流
CODE SEGMENT
    ASSUME CS:CODE,DS:DATA,ES:EXTRA,SS:STACK_SEG

;#####音乐地址#####
    MUSIC_ADD MACRO SOUNDCODE 
        CMP SOUNDCODE,1
        JE FIRST
        CMP SOUNDCODE,2
        JE SECOND
        CMP SOUNDCODE,3
        JE THIRD
        CMP SOUNDCODE,4
        JE FOURTH
    FIRST:    
        LEA SI,MUSIC_1_FREG
        LEA BP,DS:MUSIC_1_TIME
        JMP END_MUSIC_ADD
    SECOND:
        LEA SI, MUSIC_2_FREG
        LEA BP,DS:MUSIC_2_TIME
        JMP END_MUSIC_ADD
    THIRD:
        LEA SI, MUSIC_3_FREG
        LEA BP,DS:MUSIC_3_TIME
        JMP END_MUSIC_ADD
    FOURTH:
        LEA SI, MUSIC_4_FREG
        LEA BP,DS:MUSIC_4_TIME
        JMP END_MUSIC_ADD
    END_MUSIC_ADD:
        MOV SOUNDCODE,-1
    ENDM
    
    ;########发声过程########
    ;使用前先调用MUSIC_ADD SOUNDCODE，设置音乐地址
    ;定时器42H初始化及声音输出端口61H设置
    SOUND PROC NEAR 
        PUSH AX 
        PUSH BX
        PUSH CX
        PUSH DX
        PUSH DI
        MOV AL,0B6H
        OUT 43H,AL
        MOV DX,12
        MOV AX,348CH 
        DIV DI
        OUT 42H,AL
        MOV AL,AH 
        OUT 42H,AL
        IN AL,61H 
        MOV AH,AL 
        OR AL,3
        OUT 61H,AL
    TIMER:
        MOV CX,500
        CALL TIMERF
    DELAY:
        DEC BX
        JNZ TIMER
        MOV AL,AH
        OUT 61H,AL
        POP DI
        POP DX
        POP CX
        POP BX
        POP AX
        RET
    SOUND ENDP

    TIMERF PROC NEAR
        PUSH AX
    TIMERF1:
        IN AL,61H 
        AND AL,10H 
        CMP AL,AH 
        JE TIMERF1
        MOV AH,AL
        LOOP TIMERF1
        POP AX
        RET
    TIMERF ENDP
    ;音乐
    MAKE_SOUND PROC NEAR
            XOR AX,AX 
            MUSIC_ADD SOUNDCODE
    FREG:
        MOV DI,[SI]
        CMP DI,0FFFFH
        JE END_MUS 
        MOV BX,DS:[BP]
        CALL SOUND
        ADD SI,2
        ADD BP,2
        JMP FREG
    END_MUS:
        RET
    MAKE_SOUND ENDP

    ;########判断SI是否越界,DI为0表示越界########
    ;参数：偏移量SI
    CHECK_OUT_BOUND PROC
        PUSH DX
        PUSH CX
        PUSH AX
        MOV DI,0
        ;判断是否小于0
        CMP SI,0
        JL  END_CHECK
        ;判断是否大于等于REAL_SIZE
        CMP SI,REAL_SIZE
        JGE END_CHECK
        ;没越界将DI设置为0FFFFH
        MOV DI,0FFFFH
        END_CHECK:
            POP AX
            POP CX
            POP DX
            RET
    CHECK_OUT_BOUND ENDP


    ;########初始化MINE_TABLE过程########
    MINE_TABLE_GENERATE PROC
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DS
    MOV CL, MINE_NUM 
    NEXT41: 
            GET_RAND    ;产生从1到199之间的随机数作为雷的坐标,放于BX
            ;如果生成的是以前已经是雷的位置，则循环
            CMP MINE_TABLE[BX],0FFH
            JZ  NEXT41
            MOV MINE_TABLE[BX],0FFH
            DEC CL
            CMP CL,0
            JZ  END_MINE_TABLE_GENERATE
            JMP NEXT41
    END_MINE_TABLE_GENERATE:
    POP DS
    POP SI
    POP DX
    POP CX
    POP BX
    POP AX
    RET
    MINE_TABLE_GENERATE ENDP


    ;########初始化JUDGE_TABLE过程########
    JUDGE_TABLE_GENERATE PROC
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DS
    ;DX行，CX列
    MOV DX,0
    MOV CX,0
        LOOP10:
            MOV CX,0
            LOOP11:
                POSITION_REC DX,CX
                ;判断是否为0FFH
                CMP MINE_TABLE[SI],0FFH
                ;是的话将其周边的八个单元格都+1
                JZ  ADD_ALL
                MID_END_JUDGE:
                INC CX
                CMP CX,REAL_WIDTH
                JNZ LOOP11 
                JMP MID
                ADD_ALL:
                ;若为0FFH则将其周边的八个单元格都+1
                PUSH DX
                PUSH CX
                ;[DX-1][CX-1]
                DEC DX
                DEC CX
                CALL ADD_TABLE
                ;[DX-1][CX]
                INC CX
                CALL ADD_TABLE
                ;[DX-1][CX+1]
                INC CX
                CALL ADD_TABLE
                ;[DX][CX+1]
                INC DX
                CALL ADD_TABLE
                ;[DX+1][CX+1]
                INC DX
                CALL ADD_TABLE
                ;[DX+1][CX]
                DEC CX
                CALL ADD_TABLE
                ;[DX+1][CX-1]
                DEC CX
                CALL ADD_TABLE
                ;[DX][CX-1]
                DEC DX
                CALL ADD_TABLE
                POP CX
                POP DX
                JMP MID_END_JUDGE
                
            MID:    
            INC DX
            CMP DX,REAL_HEIGHT
            JNZ LOOP10_JUMP

    END_JUDGE_TABLE_GENERATE: 
        POP DS
        POP SI
        POP DX
        POP CX
        POP BX
        POP AX
        RET
    LOOP10_JUMP:JMP LOOP10
    JUDGE_TABLE_GENERATE ENDP

    ;########判断TABLE_X+TABLE_Y的奇数偶数,奇数TEMP_X为0偶数为1########
    JUDGE_Parity PROC
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        ;AX=TABLE_X+TABLE_Y;AH=AX%2
        MOV AX,TABLE_X
        ADD AX,TABLE_Y
        MOV BL,2
        DIV BL
        ;置TEMP_X为0
        MOV TEMP_X,0
        ;判断奇数偶数
        CMP AH,0
        JZ END_JUDGE_Parity
        MOV TEMP_X,1
        END_JUDGE_Parity:
        POP DX
        POP CX
        POP BX
        POP AX
        RET
    JUDGE_Parity ENDP

    ;########核心过程：把所有表清零########
    CLEAR_TABLE PROC
        PUSH BX
        MOV BX,0
        LOOP5:
            MOV MINE_TABLE[BX],0
            MOV VISIBLE_TABLE[BX],0
            MOV JUDGE_TABLE[BX],0
            INC BX
            CMP BX,200
            JNZ LOOP5
        POP BX
        RET
    CLEAR_TABLE ENDP

    ;########把TABLE的[DX][CX]+1########
    ;参数：DX,CX
    ADD_TABLE PROC 
        PUSH DX
        PUSH CX
        POSITION_REC DX,CX
        ;判断是否越界
        CMP DX,0
        JL END_ADD_TABLE
        CMP CX,0
        JL END_ADD_TABLE
        CMP DX,REAL_HEIGHT
        JGE END_ADD_TABLE
        CMP CX,REAL_WIDTH
        JGE END_ADD_TABLE
        ;没越界+1
        ADD JUDGE_TABLE[SI],1
        END_ADD_TABLE:
        POP CX
        POP DX
        RET
    ADD_TABLE ENDP

    ;########核心过程：根据定义的三个表绘制图形界面########
    ;变量10*20的表格,首先查看VISIBLE_TABLE,若不可见则显示蓝色白色相间
    ;方格，若可见则显示灰色方格并根据JUSGE_TABLE显示数字，若标记为雷则显示绿色方格
    DRAW_SCREEN PROC
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        ;从(0,0)开始遍历
        MOV TABLE_X,0
        MOV TABLE_Y,0
        ;二重循环
        LOOP1:
            MOV AX,REAL_WIDTH
            DEC AX
            MOV TABLE_Y,AX
            LOOP2:
                ;TABLE_Y*2
                MOV DX,TABLE_Y
                SAL DX,1
                MOV TEMP_Y,DX
                ;获取当前偏移量
                POSITION_REC    TABLE_X,TABLE_Y
                ;查看VISIBLE_TABLE[TABLE_X][TABLE_Y]值
                MOV AL,VISIBLE_TABLE[SI]
                ;若不可见则显示蓝色白色相间方格
                CMP AL,0
                JZ  NORMAL
                ;若可见则显示灰色方格
                CMP AL,0FFH
                JZ SHOW_JUMP
                ;若标记为雷则显示绿色方格
                CMP AL,0FH
                JZ SET_MINE_JUMP
                SHOW_JUMP:  JMP SHOW
                SET_MINE_JUMP:  JMP SET_MINE
                NORMAL:
                    ;将TABLE_X,TABLE_Y相加，判断奇数偶数,奇数TEMP_X为0偶数为1
                    CALL JUDGE_Parity
                    CMP TEMP_X,0
                    JZ NORMAL1
                    DRAW_SQUARE TABLE_X,TEMP_Y,' ',GREEN
                    JMP MID_END1
                NORMAL1:
                    DRAW_SQUARE TABLE_X,TEMP_Y,' ',WHITE
                    JMP MID_END1
                SHOW:
                    ;将JUDGE_TABLE中的数字转换成字符
                    MOV BL,JUDGE_TABLE[SI]
                    ; MOV BL,MINE_TABLE[SI]
                    ADD BL,'0'
                    DRAW_SQUARE TABLE_X,TEMP_Y,BL,GRAY
                    JMP MID_END1
                SET_MINE:
                    DRAW_SQUARE TABLE_X,TEMP_Y,' ',RED
                MID_END1:
                DEC TABLE_Y
                CMP TABLE_Y,0
                JGE LOOP2_JUMP
            INC TABLE_X
            MOV AX,REAL_HEIGHT
            CMP TABLE_X,AX
            JNZ LOOP1_JUMP
        ;移动光标
        MOV AH,2
        MOV BH,0
        MOV DH,12
        MOV DL,0
        INT 10H
        ;打印MINE_LEFT
        LEA DX,MINE_LEFT_PIC  ;打印MINE_LEFT界面
        MOV AH,09H
        INT 21H 
        ;BH存取10位ASCII码，BL存取个位
        MOV CL,MINE_LEFT
        MOV BH,'0'
        MOV BL,'0'
        MINE_TEN0:
            CMP CL,10
            JGE MINE_TEN1
        ADD BL,CL
        ;打印BH
        MOV DL,BH
        MOV AH,2
        INT 21H
        ;打印BL
        MOV DL,BL
        MOV AH,2
        INT 21H
        ;打印用时
        ;移动光标
        MOV AH,2
        MOV BH,0
        MOV DH,14
        MOV DL,0
        INT 10H
        LEA DX,SECOND_USED_PIC  ;打印用时界面
        MOV AH,09H
        INT 21H 
        ;BH存取10位ASCII码，BL存取个位
        MOV CX,MAX_SECOND
        SUB CX,SECOND_USED
        MOV BH,'0'
        MOV BL,'0'
        MINE_TEN01:
            CMP CL,10
            JGE MINE_TEN11
        ADD BL,CL
        ;打印BH
        MOV DL,BH
        MOV AH,2
        INT 21H
        ;打印BL
        MOV DL,BL
        MOV AH,2
        INT 21H
END_DRAW_SCREEN:
        POP DX
        POP CX
        POP BX
        POP AX
        RET
        LOOP2_JUMP: JMP LOOP2
        LOOP1_JUMP: JMP LOOP1
        MINE_TEN1:
            ADD BH,1
            SUB CL,10
            JMP MINE_TEN0
        MINE_TEN11:
            ADD BH,1
            SUB CL,10
            JMP MINE_TEN01
    DRAW_SCREEN ENDP

    ;########核心过程：DFS递归函数########
    ;DFS递归函数，将目前的ROW,COL存在DX,CX,将当前坐标的VISIBLE[DX][CX]设置为FFH，若其的JUDGE表中的
    ;值为0，则将其周边的八个单元格都递归调用本函数
    DFS PROC
        PUSH DX
        PUSH CX
        PUSH SI
        ;判断DX,CX是否越界，若越界则结束
        CMP DX,0
        JL DFS_OVER
        CMP CX,0
        JL DFS_OVER
        CMP DX,REAL_HEIGHT
        JGE DFS_OVER
        CMP CX,REAL_WIDTH
        JGE DFS_OVER
        ;获取TABLE[DX],[CX]的偏移量存在SI中
        POSITION_REC DX,CX
        ;检测是否已经可视(右键设置雷或者已经点开)，可视则结束
        CMP VISIBLE_TABLE[SI],0FFH
        JZ DFS_OVER
        CMP VISIBLE_TABLE[SI],0FH
        JZ DFS_OVER
        ;将VISIBLE_TABLE[DX][CX],设置为FFH
        MOV VISIBLE_TABLE[SI],0FFH
        ;判断JUDGE_TABLE[DX][CX]是否为0
        CMP JUDGE_TABLE[SI],0
        JNZ DFS_OVER
        ;若为0则将其周边的八个单元格都递归调用本函数
        ;[DX-1][CX-1]
        DEC DX
        DEC CX
        CALL DFS
        ;[DX-1][CX]
        INC CX
        CALL DFS
        ;[DX-1][CX+1]
        INC CX
        CALL DFS
        ;[DX][CX+1]
        INC DX
        CALL DFS
        ;[DX+1][CX+1]
        INC DX
        CALL DFS
        ;[DX+1][CX]
        DEC CX
        CALL DFS
        ;[DX+1][CX-1]
        DEC CX
        CALL DFS
        ;[DX][CX-1]
        DEC DX
        CALL DFS
    ;结束DFS
    DFS_OVER:    
        POP SI
        POP CX
        POP DX
        RET
    DFS ENDP

START:
        ;初始化代码段
        MOV AX,DATA
        MOV DS,AX
        MOV AX,EXTRA
        MOV ES,AX
        MOV AX,STACK_SEG
        MOV SS,AX
        ;###########绘制欢迎界面,等待任意键输入###########
        SETSCREEN;刷新页面
        LEA DX,HELLOPIC   ;打印欢迎界面
        MOV AH,09H
        INT 21H   
        MOV AX,1;等待任意键入
        INT 16H
        ;发声
        MOV SOUNDCODE,1
        CALL MAKE_SOUND 
        ;###########绘制难度选择界面,等待输入难度(A,B,C)###########
CHOOSE_HARD:
        ;进入难度选择界面
        SETSCREEN;刷新页面
        LEA DX,HARD_CHOICEPIC   ;打印难度选择界面
        MOV AH,09H
        INT 21H   
        GET_HARD_LEVEL  HARD_LEVEL;根据键入获取难度等级
        ;发声
        MOV SOUNDCODE,2
        CALL MAKE_SOUND 
        ;###########初始化参数###########
        ;清空表格
        CALL CLEAR_TABLE
        ;设置雷数和长宽和时间限制
        SET_MINE_NUM HARD_LEVEL,MINE_NUM
        MOV CL,MINE_NUM
        ;设置剩余雷数
        MOV MINE_LEFT,CL
        ;初始化表格
        CALL MINE_TABLE_GENERATE 
        CALL JUDGE_TABLE_GENERATE
        ;初始化时间
        GET_TIME START_MIN,START_SECOND
        ;###########根据定义的三个表绘制图形界面###########
        SETSCREEN
REFRESHSCREEN:
        GET_TIME NOW_MIN,NOW_SECOND
        CALL DRAW_SCREEN
        ;###########判断时间是否耗尽###########
        GET_SECOND_USED
        MOV AX,MAX_SECOND
        CMP SECOND_USED,AX
        JG TIME_OUT_JUMP
        JMP GET_MOUSE0
        TIME_OUT_JUMP:
            JMP TIME_OUT
        GET_MOUSE0:
        ;###########获取鼠标坐标和左右键,并进行相应处理###########
        ;获取鼠标坐标(TABLE_X,TABLE_Y)和左右键(SI)
        GET_MOUSE TABLE_Y,TABLE_X
        POS_TURN  TABLE_X,TABLE_Y
        ;判断TABLE_Y是否越界
        MOV AX,REAL_WIDTH
        CMP TABLE_Y,AX
        JGE REFRESHSCREEN_JUMP0
        JMP MOUSE_HANDLE
        REFRESHSCREEN_JUMP0:
        JMP REFRESHSCREEN
        MOUSE_HANDLE:
        ;判断左右键
        CMP SI,0
        JZ  LEFT_MOUSE
        CMP SI,0FFFFH
        JZ  RIGHT_MOUSE
        JMP REFRESHSCREEN
        LEFT_MOUSE:
            ;左键处理
            POSITION_REC TABLE_X,TABLE_Y
            ;判断是否点击到雷，若点击到，则转失败处理
            MOV AL,MINE_TABLE[SI]
            CMP AL,0FFH
            JZ  LOOS_END_JUMP
            ;没点击到雷
            ;判断VISIBLE_TABLE是否已经为0FH
            CMP VISIBLE_TABLE[SI],0FH
            ;不是0FH，进行DFS处理
            JNZ DFS_PROCESS
            ;已经为0FH，将其变为0FFH
            MOV VISIBLE_TABLE[SI],0FFH
            DFS_PROCESS:
            ;用DX,CX存储TABLE_X,TABLE_Y
            MOV DX,TABLE_X
            MOV CX,TABLE_Y
            CALL DFS
            JMP REFRESHSCREEN
            LOOS_END_JUMP:
                JMP LOOS_END

        RIGHT_MOUSE:
            ;右键处理
            POSITION_REC TABLE_X,TABLE_Y
            ;判断是否点击到雷，若没点击到，则不改变MINE_LEFT
            MOV AL,MINE_TABLE[SI]
            CMP AL,0FFH
            JZ RIGHT_MOUSE_MINE
            ;改变VISIBLE_TABLE
            MOV VISIBLE_TABLE[SI],0FH
            JMP REFRESHSCREEN
            RIGHT_MOUSE_MINE:
            ;点击到雷
            ;判断VISIBLE_TABLE是否已经为0FH
            CMP VISIBLE_TABLE[SI],0FH
            ;已经为0FH，直接结束
            JZ  REFRESHSCREEN_JUMP
            ;不是则改变VISIBLE_TABLE，MINE_LEFT
            MOV VISIBLE_TABLE[SI],0FH
            SUB MINE_LEFT,1
            ;判断是否还有雷剩余
            CMP MINE_LEFT,0
            ;还有雷
            JNZ REFRESHSCREEN_JUMP
            ;没有雷
            JMP WIN_END
            REFRESHSCREEN_JUMP:
                JMP REFRESHSCREEN
        WIN_END:
            ;打印胜利画面
            SETSCREEN
            LEA DX,WIN_PIC   
            MOV AH,09H
            INT 21H
            ; ;发声
            MOV SOUNDCODE,4
            CALL MAKE_SOUND 
            ;等待键入
            ;键入
            BEGIN1:
            MOV AH,0
            INT 16H
            ;判断键入字符是否在j-k之间，若不在循环键入
            CMP AL,'j'
            JL  BEGIN1
            CMP AL,'k'
            JG  BEGIN1
            ;把AL-'j'
            SUB AL,'j'
            ;键入J重新进入难度选择界面
            CMP AL,0
            JZ  CHOOSE_HARD_JUMP1
            ;键入K结束程序
            JMP END_ALL
            CHOOSE_HARD_JUMP1:
                JMP CHOOSE_HARD
        LOOS_END:
            ;打印失败画面
            SETSCREEN
            LEA DX,LOOSE_PIC
            MOV AH,09H
            INT 21H
            ; ;发声
            MOV SOUNDCODE,1
            CALL MAKE_SOUND 
            ;等待键入
            ;键入
            BEGIN2:
            MOV AH,0
            INT 16H
            ;判断键入字符是否在j-k之间，若不在循环键入
            CMP AL,'j'
            JL  BEGIN2
            CMP AL,'k'
            JG  BEGIN2
            ;把AL-'j'
            SUB AL,'j'
            ;键入J重新进入难度选择界面
            CMP AL,0
            JZ  CHOOSE_HARD_JUMP2
            ;键入K结束程序
            JMP END_ALL
            CHOOSE_HARD_JUMP2:
                JMP CHOOSE_HARD
        TIME_OUT:
            ;打印时间耗尽画面
            SETSCREEN
            LEA DX,TIME_OUT_PIC
            MOV AH,09H
            INT 21H
            ; ;发声
            MOV SOUNDCODE,3
            CALL MAKE_SOUND 
            ;等待键入
            ;键入
            BEGIN3:
            MOV AH,0
            INT 16H
            ;判断键入字符是否在j-k之间，若不在循环键入
            CMP AL,'j'
            JL  BEGIN1
            CMP AL,'k'
            JG  BEGIN1
            ;把AL-'j'
            SUB AL,'j'
            ;键入J重新进入难度选择界面
            CMP AL,0
            JZ  CHOOSE_HARD_JUMP3
            ;键入K结束程序
            JMP END_ALL
            CHOOSE_HARD_JUMP3:
                JMP CHOOSE_HARD
        END_ALL:
            ;结束程序
            MOV AH,4CH
            INT 21H
CODE ENDS
        END START


